Skip to content

H-6800: Improve Petrinaut Voice turn-taking and answer provenance - #9512

Open
kostandinang wants to merge 10 commits into
kah-6763-temporary-brunch-askfrom
kah-6800-improve-petrinaut-voice-turn-taking-and-answer-provenance
Open

H-6800: Improve Petrinaut Voice turn-taking and answer provenance#9512
kostandinang wants to merge 10 commits into
kah-6763-temporary-brunch-askfrom
kah-6800-improve-petrinaut-voice-turn-taking-and-answer-provenance

Conversation

@kostandinang

@kostandinang kostandinang commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

🌟 What is the purpose of this PR?

Make Voice interruption predictable and keep Voice attribution next to the interactive-tool answer it describes. Users can explicitly take the turn while the assistant prepares or speaks, and the microphone resumes only after Realtime confirms that canceled input and output have cleared.

This PR collects the independently useful UI/UX improvements discovered while testing H-6763. It does not change semantic VAD eagerness, add full duplex, or provide durable elicitation or canvas writes.

🔗 Related links

🚫 Blocked by

🔍 What does this change?

  • Adds an accessible Your turn action while Voice prepares or speaks, styled consistently with the Voice dock controls.
  • Clears input and output audio, cancels queued or active speech without disconnecting Realtime, and waits for cancellation acknowledgements before reopening the microphone.
  • Ignores speech captured before the handoff while keeping the interrupted question visible and repeatable.
  • Lets interactive tools opt into a backwards-compatible answer-prefix slot so the Voice provenance chip appears immediately before submitted answers; other widgets retain the trailing fallback.
  • Updates the Brunch ask widget, Petrinaut AI Assistant documentation, package guidance, release notes, and controller/session/integration/UI tests.
🏗️ Agent notes

Imperative: Preserve the useful Voice interaction improvements exposed by H-6763 as a reviewable change independent of the temporary Brunch compatibility mechanism.

Throughline: Voice dock → composer control → host Voice control → turn controller → OpenAI Realtime cancellation acknowledgements → listening resumes. Interactive-tool rendering independently projects Voice provenance immediately before an opted-in answer.

Proof: Controller, Realtime session, host-control, preview-integration, and UI tests cover successful and failed handoffs, stale audio rejection, cancellation ordering, repeatability, inline provenance, and backwards-compatible fallback rendering.

Constraints: Semantic VAD remains at low eagerness so natural thinking pauses are not prematurely committed. The handoff does not disconnect Realtime or enable full duplex. Other interactive tools retain their existing provenance placement unless they opt into the prefix slot.

Fog-line: The durable elicitation and canvas-write architecture remains outside this PR. These interaction behaviors must survive any later replacement of the temporary parent shim.

Stop or reorient: Restack or migrate these behaviors onto the durable Voice branch before #9507 is removed.

Pre-Merge Checklist 🚀

🚢 Has this modified a publishable library?

This PR:

  • modifies the npm-publishable @hashintel/petrinaut library and includes its required patch changeset

📜 Does this require a change to the docs?

The changes in this PR:

  • require changes to docs which are made as part of this PR

🕸️ Does this require a change to the Turbo Graph?

The changes in this PR:

  • do not affect the execution graph

⚠️ Known issues

  • Voice remains intentionally half-duplex; Your turn is an explicit handoff rather than simultaneous conversation.
  • This PR does not change semantic VAD eagerness; it remains low.
  • Durable elicitation and Petrinaut canvas writes remain outside this stack.

🐾 Next steps

🛡 What tests cover this?

  • Focused Voice controller, Realtime session, preview integration, session-state, and host-control tests
  • Focused Petrinaut AI Assistant and interactive-tool UI tests
  • yarn workspace @apps/petrinaut-website lint:tsc
  • yarn workspace @hashintel/petrinaut lint:tsc
  • yarn workspace @apps/petrinaut-website lint:eslint — 0 errors; 1 pre-existing warning
  • yarn workspace @hashintel/petrinaut lint:eslint — 0 errors or warnings
  • Focused oxfmt, Markdown, architecture-doc, and git diff --check validation

❓ How to test this?

  1. Start a Voice interview and wait for the assistant to prepare or speak a question.
  2. Select Your turn and confirm speech stops without disconnecting.
  3. Speak only after the handoff completes and confirm pre-handoff audio is not submitted.
  4. Confirm Repeat question remains available for the interrupted question.
  5. Submit a Voice answer through the Brunch ask widget and confirm the Voice chip appears immediately before the answer.

📹 Demo

Not recorded.

@vercel

vercel Bot commented Sep 2, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
hash Ready Ready Preview Sep 3, 2026 12:43pm UTC
petrinaut Ready Ready Preview Sep 3, 2026 12:43pm UTC
petrinaut-docs Ready Ready Preview Sep 3, 2026 12:43pm UTC
1 Skipped Deployment
Project Deployment Actions Updated
hashdotdesign-tokens Ignored Ignored Preview Sep 3, 2026 12:43pm UTC

Request Review

@cursor

cursor Bot commented Sep 2, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Changes real-time voice cancellation, microphone gating, and chat metadata attribution—behavioral paths that are easy to get wrong under race conditions, though coverage is extensive in session/controller/UI tests.

Overview
Adds a Your turn control in the Voice dock (and takeTurn / canTakeTurn on host session APIs) so users can interrupt assistant speech or pending delivery without ending the session. The website turn controller drives a stricter Realtime cancelOutput() that clears input/output buffers, awaits provider acknowledgements, and only then re-enables the mic while discarding utterances that started before the handoff.

Voice provenance moves from a text Voice chip to a compact waveform icon on spoken user messages. Interactive tools can opt in with supportsSubmittedOutputProvenance and submittedOutputProvenance so Brunch ask (and similar widgets) place that icon inside the submitted answer bubble; others keep the trailing fallback. Assistant message metadata now tracks voiceToolCallIds (with rollback when a tool submit fails) so multiple voice-answered tools on one turn stay attributed correctly.

Supporting refactors split Brunch/voice modules for Fast Refresh (widget, disclosure, config loader, getBrunchVoiceMode) and replace diagnostics transport refs with DiagnosticsTransportState in the AI assistant panel.

Reviewed by Cursor Bugbot for commit d13535d. Bugbot is set up for automated code reviews on this repo. Configure here.

@kostandinang kostandinang changed the title Add manual Voice turn handoff H-6800: Improve Petrinaut Voice turn-taking and answer provenance Sep 2, 2026
@kostandinang
kostandinang changed the base branch from kah-6763-temporary-brunch-ask to graphite-base/9512 September 2, 2026 20:12
kostandinang and others added 6 commits September 2, 2026 22:26
Keep the controller paused until Realtime confirms its input and output buffers are clear, preventing resumed speech from being discarded.

Co-authored-by: Cursor <cursoragent@cursor.com>
@kostandinang
kostandinang force-pushed the kah-6800-improve-petrinaut-voice-turn-taking-and-answer-provenance branch from 7d768c7 to 4422bfc Compare September 2, 2026 21:07
@kostandinang
kostandinang requested a review from a team as a code owner September 2, 2026 21:07
@kostandinang
kostandinang changed the base branch from graphite-base/9512 to kah-6763-temporary-brunch-ask September 2, 2026 21:07
Comment thread apps/petrinaut-website/src/main/app/voice-interview/voice-turn-controller.ts Outdated
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit d13535d. Configure here.

(toolCallId) => toolCallId !== params.toolCallId,
);
if (remainingVoiceToolCallIds.length === 0) {
return { ...message, metadata: previousMetadata };

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rollback restores stale voice IDs

Medium Severity

When overlapping Voice tool submissions both reject, the later rollback can restore previousMetadata after sibling IDs were already removed. That puts a failed toolCallId back into voiceToolCallIds, so a later typed retry can still receive Voice provenance.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit d13535d. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/apps area/infra Relates to version control, CI, CD or IaC (area) area/libs Relates to first-party libraries/crates/packages (area) type/eng > frontend Owned by the @frontend team

Development

Successfully merging this pull request may close these issues.

1 participant